package com.tongweb.tw.thanos;

import com.tongweb.catalina.deploy.NamingResourcesImpl;
import com.tongweb.catalina.startup.Catalina;
import com.tongweb.catalina.startup.ThanosCatalina;
import com.tongweb.common.ThreadLocalKeeper;
import com.tongweb.config.ConfigTool;
import com.tongweb.config.bean.*;
import com.tongweb.deploy.interfaces.AutoDeployService;
import com.tongweb.deploy.interfaces.DeployException;
import com.tongweb.deploy.interfaces.DeployInfo;
import com.tongweb.deploy.interfaces.DeployJMXServer;
import com.tongweb.deploy.interfaces.Deployer;
import com.tongweb.deploy.interfaces.HotDeployService;
import com.tongweb.tongejb.Core;
import com.tongweb.web.sharedlib.SharedLibCentralService;
import com.tongweb.web.thanos.startup.LastStepListener;
import com.tongweb.web.util.descriptor.web.ContextResource;
import com.tongweb.web.util.descriptor.web.NamingResources;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import java.util.concurrent.CountDownLatch;

public class TongwebLoader implements LastStepListener {
    private static final boolean ENABLE_PARALLEL = Boolean.valueOf(System.getProperty("enableParallelDeploy", "false")).booleanValue();

    private static TongwebLoader tl;

    private DeployInfo di;

    private AutoDeployService autoDeployService;

    private HotDeployService hotDeployService;

    private Deployer deployer;

    private DeployJMXServer deployJMXServer;

    public static synchronized TongwebLoader getInstance() throws ClassNotFoundException, InstantiationException, IllegalAccessException, SecurityException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException {
        if (tl == null) {
            tl = new TongwebLoader();
            Class<?> deployerClass = Thread.currentThread().getContextClassLoader().loadClass("com.tongweb.deploy.TongWebDeployer");
            tl.setDeployer((Deployer) deployerClass.newInstance());
            Class<?> autodeployserviceClass = Thread.currentThread().getContextClassLoader().loadClass("com.tongweb.deploy.autodeploy.TongWebAutoDeployService");
            Method method = autodeployserviceClass.getMethod("getInstance", null);
            tl.setAutoDeployService((AutoDeployService) method.invoke(null, null));
            Class<?> hotdeployserviceClass = Thread.currentThread().getContextClassLoader().loadClass("com.tongweb.deploy.hotdeploy.TongWebHotDeployService");
            Method method1 = hotdeployserviceClass.getMethod("getInstance", null);
            tl.setHotDeployService((HotDeployService) method1.invoke(null, null));
            Class<?> deployJMXServerClass = Thread.currentThread().getContextClassLoader().loadClass("com.tongweb.deploy.commands.jmx.TongWebDeployJMXServer");
            Method method2 = deployJMXServerClass.getMethod("getInstance", null);
            tl.setDeployJMXServer((DeployJMXServer) method2.invoke(null, null));
        }
        return tl;
    }

    public DeployJMXServer getDeployJMXServer() {
        return this.deployJMXServer;
    }

    public void setDeployJMXServer(DeployJMXServer deployJMXServer) {
        this.deployJMXServer = deployJMXServer;
    }

    public AutoDeployService getAutoDeployService() {
        return this.autoDeployService;
    }

    public void setAutoDeployService(AutoDeployService autoDeployService) {
        this.autoDeployService = autoDeployService;
    }

    public HotDeployService getHotDeployService() {
        return this.hotDeployService;
    }

    public void setHotDeployService(HotDeployService hotDeployService) {
        this.hotDeployService = hotDeployService;
    }

    public Deployer getDeployer() {
        return this.deployer;
    }

    public void setDeployer(Deployer deployer) {
        this.deployer = deployer;
    }

    public void event() throws DeployException {
        SharedLibCentralService.getInstance().initial();
        initHotDeploy();
        initDeploy();
        initAutoDeploy();
        initDeployJMXInterface();
    }

    private void synchronizationLogProperties() {
        LogService logService = ConfigTool.getRoot().getServer().getLogService();
        Properties p = new Properties();
        boolean rotation = logService.isRotation();
        p.put("rotation", Boolean.valueOf(rotation).toString());
        String limit = logService.getRotationLimit();
        p.put("rotation-limit", limit);
        String timelimit = logService.getRotationTimelimit();
        p.put("rotation-timelimit", timelimit);
        String logFile = logService.getFile();
        p.put("file", logFile);
        int count = logService.getRotationFileCount();
        p.put("rotation-file-count", Integer.valueOf(count).toString());
        String logFormat = logService.getLogFormat();
        p.put("log-format", logFormat);
        boolean verbose = logService.isVerbose();
        p.put("verbose", Boolean.valueOf(verbose).toString());
        ModuleLogLevels moduleLogLevels = logService.getModuleLogLevels();
        p.putAll(convertObjectPropertiesToMap(moduleLogLevels, "levels."));
    }

    private Map<String, String> convertObjectPropertiesToMap(Object object, String propertyNamePrefix) {
        Map<String, String> result = new HashMap<String, String>();
        Method[] methods = object.getClass().getMethods();
        for (Method method : methods) {
            String methodName = method.getName();
            if (methodName.equals("getProperty")) {
                if (object instanceof ModuleLogLevels) {
                    List<Property> properties = ((ModuleLogLevels) object).getProperty();
                    if (properties != null && properties.size() >= 0)
                        for (Property prop : properties)
                            result.put(prop.getName(), prop.getValue());
                }
            } else if (methodName.startsWith("get") && !methodName.equals("getClass") && (method.getParameterTypes()).length == 0) {
                String propertyName = methodName.substring(3, 4).toLowerCase() + methodName.substring(4);
                propertyName = converPropertyName(propertyName);
                try {
                    Object value = method.invoke(object, new Object[0]);
                    result.put(propertyNamePrefix + propertyName, (String) value);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return result;
    }

    private String converPropertyName(String name) {
        StringBuffer result = new StringBuffer("");
        for (char ch : name.toCharArray()) {
            if (Character.isUpperCase(ch)) {
                result.append("-");
                result.append(Character.toLowerCase(ch));
            } else {
                result.append(ch);
            }
        }
        return result.toString();
    }

    private static boolean deleteDir(File dir, List<String> precompileApps) {
        if (dir.isDirectory()) {
            String[] children = dir.list();
            for (int i = 0; i < children.length; i++) {
                File file = new File(dir, children[i]);
                if (file.isFile()) {
                    String fName = file.getName();
                    String path = file.getAbsolutePath();
                    boolean del = true;
                    if (!precompileApps.isEmpty())
                        if (fName.endsWith(".class") || fName.endsWith(".java"))
                            for (String notClean : precompileApps) {
                                if (path.contains(notClean)) {
                                    del = false;
                                    break;
                                }
                            }
                    if (del)
                        file.delete();
                } else if (file.isDirectory()) {
                    deleteDir(file, precompileApps);
                }
            }
        }
        return dir.delete();
    }

    public void cleanTempDirectory() {
        List<String> precompileApps = precompileApp();
        deleteDir(new File(System.getProperty("tongweb.base") + File.separator + "temp"), precompileApps);
        if (precompileApps.isEmpty())
            (new File(System.getProperty("tongweb.base") + File.separator + "temp")).mkdir();
    }

    private List<String> precompileApp() {
        List<String> precompileApps = new ArrayList<String>();
        try {
            Apps apps = ConfigTool.getRoot().getApps();
            if (null != apps) {
                List<WebApp> webApps = apps.getWebApp();
                for (WebApp webApp : webApps) {
                    if (webApp.isJspCompile()) {
                        String[] vsNames = webApp.getVsNames().split(",");
                        String name = webApp.getContextRoot();
                        if (name.startsWith("/"))
                            name = name.substring(1);
                        for (String vsName : vsNames)
                            addPathToList(precompileApps, vsName, name);
                    }
                }
                List<EarApp> earApps = apps.getEarApp();
                for (EarApp earApp : earApps) {
                    List<WebappModules> webappModules = earApp.getWebappModules();
                    if (earApp.isJspCompile() && !webappModules.isEmpty()) {
                        String[] vsNames = earApp.getVsNames().split(",");
                        for (WebappModules modules : webappModules) {
                            String name = modules.getValue();
                            if (null != name && !"".equals(name)) {
                                String[] tmp = name.split(":");
                                if (tmp.length > 0) {
                                    name = tmp[0];
                                    for (String vsName : vsNames)
                                        addPathToList(precompileApps, vsName, name);
                                }
                            }
                        }
                    }
                }
            }
        } catch (Exception exception) {
        }
        return precompileApps;
    }

    private void addPathToList(List<String> precompileApps, String vsName, String appName) {
        String workDir = "work" + File.separator + vsName + File.separator + appName;
        if (!precompileApps.contains(workDir))
            precompileApps.add(workDir);
    }

    public void initDeploy() {
        cleanTempDirectory();
        try {
            Apps apps = ConfigTool.getRoot().getApps();
            List<Object> appList = new ArrayList();
            if (apps != null)
                appList = ConfigTool.getChildren(apps);
            AppComparator ac = new AppComparator();
            Collections.sort(appList, ac);
            Iterator<Object> itapps = appList.iterator();

           // slib();
           // ENABLE_PARALLEL
            if (true) {
                Map<Integer, List<DeployInfo>> deployMap = new TreeMap<Integer, List<DeployInfo>>();
                while (itapps.hasNext()) {
                    Object bean = itapps.next();
                    DeployInfo di = new DeployInfo();
                    copyAttributeToDeployInfo(bean, di);
                    if (di.getVersion() == null || di.getVersion() == "" || (di.getVersion() != "" && di.getVersion() != null && !"retired".equals(di.getRetireState()))) {
                        di.setCommandFrom("restart");
                        List<DeployInfo> diList = deployMap.get(Integer.valueOf(di.getDeployOrder()));
                        if (diList != null) {
                            diList.add(di);
                        } else {
                            diList = new ArrayList<DeployInfo>();
                            diList.add(di);
                        }
                        deployMap.put(Integer.valueOf(di.getDeployOrder()), diList);
                    }
                }
                Iterator<Integer> mapit = deployMap.keySet().iterator();
                while (mapit.hasNext()) {
                    List<DeployInfo> diList = deployMap.get(mapit.next());
                    CountDownLatch latch = new CountDownLatch(diList.size());
                    for (DeployInfo deployInfo : diList) {
                        DeployThread dt = new DeployThread(deployInfo, latch);
                        Thread t = new Thread(dt);
                        t.start();
                    }
                    latch.await();
                }
            } else {
                while (itapps.hasNext()) {
                    DeployInfo di = new DeployInfo();
                    Object bean = itapps.next();
                    copyAttributeToDeployInfo(bean, di);
                    if (di.getVersion() == null || di.getVersion() == "" || (di.getVersion() != "" && di.getVersion() != null && !"retired".equals(di.getRetireState()))) {
                        di.setCommandFrom("restart");
                        deployOneThread(di, null);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    class DeployThread implements Runnable {
        private DeployInfo di;
        private CountDownLatch latch;

        public DeployThread(DeployInfo di, CountDownLatch latch) {
            this.di = di;
            this.latch = latch;
        }

        public void run() {
            TongwebLoader.this.deployOneThread(this.di, this.latch);
        }
    }


    private void deployOneThread(DeployInfo di, CountDownLatch latch) {
        try {
            if (di.getDeployOrder() <= 0)
                throw new Exception(di.getName() + " 's app deployorder is a negative number or zero,skip deploy this app");
            if (di.isEnabled() || (!di.isEnabled() && di.getVersion() != null && di.getVersion() != "")) {
                if (di.isAutoDeploy()) {
                    ThreadLocalKeeper.addAutoDeploy("true");
                } else {
                    if (di.getVersion() != null && di.getVersion() != "")
                        ThreadLocalKeeper.setVersionUpdate(Boolean.valueOf(true));
                    ThreadLocalKeeper.addAutoDeploy("false");
                }
                ThreadLocalKeeper.addInitDeploy("true");
                if (di.isAutoDeploy() && !di.isDirectory() && this.autoDeployService.isAutoDeployInfoUpdated(di)) {
                    this.autoDeployService.deployOnStartup(di);
                } else {
                    this.deployer.deploy(di);
                }
                if (di.getVersion() != "" && di.getVersion() != null && "retired".equals(di.getRetireState()))
                    this.deployer.retire(di);
            } else {
                di.setStatus("stopped");
                this.deployer.addToLazyDeployMap(di);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (latch != null)
                latch.countDown();
        }
    }

    private void copyAttributeToDeployInfo(Object bean, DeployInfo di) {
        di.setFirstDeploy(false);
        if (bean instanceof WebApp) {
            di.setType("war");
            di.setName(((WebApp) bean).getName());
            if (((WebApp) bean).isIsAutodeploy()) {
                String fileLocation = ((WebApp) bean).getOriginalLocation();
                fileLocation = fileLocation.replaceAll("\\\\", "/");
                String fileName = fileLocation.substring(fileLocation.lastIndexOf("/") + 1);
                di.setFileName(fileName);
            } else {
                di.setFileName(((WebApp) bean).getName());
            }
            di.setLocation(((WebApp) bean).getLocation());
            di.setOriginalLocation(((WebApp) bean).getOriginalLocation());
            di.setDescription(((WebApp) bean).getDescription());
            String contextRoot = ((WebApp) bean).getContextRoot();
            if (contextRoot != null && contextRoot.equals("")) {
                di.setContextRoot("/");
            } else {
                di.setContextRoot(contextRoot);
            }
            System.out.println("**** contextRoot: " + contextRoot);
            di.setVsNames(((WebApp) bean).getVsNames());
            di.setDeployOrder(((WebApp) bean).getDeployOrder());
            di.setDirectory(((WebApp) bean).isIsDirectory());
            di.setEnabled(((WebApp) bean).isEnabled());
            di.setObjectType(((WebApp) bean).getObjectType());
            di.setJspCompile(((WebApp) bean).isJspCompile());
            di.setDtdValidate(((WebApp) bean).isDtdValidate());
            di.setAutoDeploy(((WebApp) bean).isIsAutodeploy());
            di.setVersion(String.valueOf(((WebApp) bean).getVersion()));
            di.setRetireState(((WebApp) bean).getRetireState());
            di.setRetireStrategy(((WebApp) bean).getRetireStrategy());
            di.setRetireTimeout((((WebApp) bean).getRetireTimeout() == null) ? 0 : ((WebApp) bean).getRetireTimeout().intValue());
            di.setVersionSerialNumber((((WebApp) bean).getVersionSerialNumber() == null) ? 0 : ((WebApp) bean).getVersionSerialNumber().intValue());
            if (((WebApp) bean).isDelegate() != null)
                di.setDelegate(((WebApp) bean).isDelegate().booleanValue());
            di.setCacheMaxSize(((WebApp) bean).getCacheMaxSize());
        } else if (bean instanceof EjbApp) {
            di.setType("ejb");
            di.setName(((EjbApp) bean).getName());
            if (((EjbApp) bean).isIsAutodeploy()) {
                String fileLocation = ((EjbApp) bean).getOriginalLocation();
                fileLocation = fileLocation.replaceAll("\\\\", "/");
                String fileName = fileLocation.substring(fileLocation.lastIndexOf("/") + 1);
                di.setFileName(fileName);
            } else {
                di.setFileName(((EjbApp) bean).getName());
            }
            di.setLocation(((EjbApp) bean).getLocation());
            di.setOriginalLocation(((EjbApp) bean).getOriginalLocation());
            di.setDescription(((EjbApp) bean).getDescription());
            di.setDeployOrder(((EjbApp) bean).getDeployOrder());
            di.setDirectory(((EjbApp) bean).isIsDirectory());
            di.setEnabled(((EjbApp) bean).isEnabled());
            di.setObjectType(((EjbApp) bean).getObjectType());
            di.setDtdValidate(((EjbApp) bean).isDtdValidate());
            di.setAutoDeploy(((EjbApp) bean).isIsAutodeploy());
            di.setVersion(((EjbApp) bean).getVersion());
            di.setRetireState(((EjbApp) bean).getRetireState());
            di.setRetireStrategy(((EjbApp) bean).getRetireStrategy());
            di.setRetireTimeout((((EjbApp) bean).getRetireTimeout() == null) ? 0 : ((EjbApp) bean).getRetireTimeout().intValue());
            di.setVersionSerialNumber((((EjbApp) bean).getVersionSerialNumber() == null) ? 0 : ((EjbApp) bean).getVersionSerialNumber().intValue());
        } else if (bean instanceof EarApp) {
            di.setType("ear");
            di.setName(((EarApp) bean).getName());
            if (((EarApp) bean).isIsAutodeploy()) {
                String fileLocation = ((EarApp) bean).getOriginalLocation();
                fileLocation = fileLocation.replaceAll("\\\\", "/");
                String fileName = fileLocation.substring(fileLocation.lastIndexOf("/") + 1);
                di.setFileName(fileName);
            } else {
                di.setFileName(((EarApp) bean).getName());
            }
            di.setLocation(((EarApp) bean).getLocation());
            di.setOriginalLocation(((EarApp) bean).getOriginalLocation());
            di.setDescription(((EarApp) bean).getDescription());
            di.setVsNames(((EarApp) bean).getVsNames());
            di.setDeployOrder(((EarApp) bean).getDeployOrder());
            di.setDirectory(((EarApp) bean).isIsDirectory());
            di.setEnabled(((EarApp) bean).isEnabled());
            di.setObjectType(((EarApp) bean).getObjectType());
            di.setJspCompile(((EarApp) bean).isJspCompile());
            di.setDtdValidate(((EarApp) bean).isDtdValidate());
            di.setDelegate(((EarApp) bean).isDelegate());
            di.setAutoDeploy(((EarApp) bean).isIsAutodeploy());
            di.setVersion(((EarApp) bean).getVersion());
            di.setRetireState(((EarApp) bean).getRetireState());
            di.setRetireStrategy(((EarApp) bean).getRetireStrategy());
            di.setRetireTimeout((((EarApp) bean).getRetireTimeout() == null) ? 0 : ((EarApp) bean).getRetireTimeout().intValue());
            di.setVersionSerialNumber((((EarApp) bean).getVersionSerialNumber() == null) ? 0 : ((EarApp) bean).getVersionSerialNumber().intValue());
            List<WebappModules> list = ((EarApp) bean).getWebappModules();
            if (!list.isEmpty()) {
                String oldDelegateList = ((WebappModules) list.get(0)).getValue();
                if (!oldDelegateList.isEmpty()) {
                    String newDelegateList = "";
                    String[] temp = oldDelegateList.split(";");
                    for (int i = 0; i < temp.length; i++)
                        newDelegateList = newDelegateList + temp[i] + ";";
                    di.setDelegateList(newDelegateList);
                }
            }
        } else if (bean instanceof ConnectorApp) {
            di.setType("rar");
            di.setName(((ConnectorApp) bean).getName());
            if (((ConnectorApp) bean).isIsAutodeploy()) {
                String fileLocation = ((ConnectorApp) bean).getOriginalLocation();
                fileLocation = fileLocation.replaceAll("\\\\", "/");
                String fileName = fileLocation.substring(fileLocation.lastIndexOf("/") + 1);
                di.setFileName(fileName);
            } else {
                di.setFileName(((ConnectorApp) bean).getName());
            }
            di.setLocation(((ConnectorApp) bean).getLocation());
            di.setOriginalLocation(((ConnectorApp) bean).getOriginalLocation());
            di.setDescription(((ConnectorApp) bean).getDescription());
            di.setThreadPool(((ConnectorApp) bean).getThreadPool());
            di.setDeployOrder(((ConnectorApp) bean).getDeployOrder());
            di.setDirectory(((ConnectorApp) bean).isIsDirectory());
            di.setEnabled(((ConnectorApp) bean).isEnabled());
            di.setObjectType(((ConnectorApp) bean).getObjectType());
            di.setDtdValidate(((ConnectorApp) bean).isDtdValidate());
            di.setAutoDeploy(((ConnectorApp) bean).isIsAutodeploy());
            di.setVersion(((ConnectorApp) bean).getVersion());
            di.setRetireState(((ConnectorApp) bean).getRetireState());
            di.setRetireStrategy(((ConnectorApp) bean).getRetireStrategy());
            di.setRetireTimeout((((ConnectorApp) bean).getRetireTimeout() == null) ? 0 : ((ConnectorApp) bean).getRetireTimeout().intValue());
            di.setVersionSerialNumber((((ConnectorApp) bean).getVersionSerialNumber() == null) ? 0 : ((ConnectorApp) bean).getVersionSerialNumber().intValue());
            if (((ConnectorApp) bean).getProperty() != null)
                di.setProperty(((ConnectorApp) bean).getProperty());
        }
    }

    private class AppComparator implements Comparator {
        private AppComparator() {
        }

        private int getConfigBeanDeployOrder(Object bean) {
            if (bean instanceof WebApp)
                return ((WebApp) bean).getDeployOrder();
            if (bean instanceof EjbApp)
                return ((EjbApp) bean).getDeployOrder();
            if (bean instanceof EarApp)
                return ((EarApp) bean).getDeployOrder();
            if (bean instanceof ConnectorApp)
                return ((ConnectorApp) bean).getDeployOrder();
            return 100;
        }

        public int compare(Object arg0, Object arg1) {
            int i = getConfigBeanDeployOrder(arg0);
            int j = getConfigBeanDeployOrder(arg1);
            if (i > j)
                return 1;
            if (i == j)
                return 0;
            return -1;
        }
    }

    public void initAutoDeploy() throws DeployException {
        try {
            AutoDeploy autodeploy = ConfigTool.getRoot().getAutoDeploy();
            this.autoDeployService.init(autodeploy.isEnabled(), autodeploy.getDir(), autodeploy.getCheckInterval(), autodeploy.isJspCompile());
            this.autoDeployService.start();
        } catch (Exception e) {
            e.printStackTrace();
            this.autoDeployService.stop();
        }
    }

    public void initHotDeploy() throws DeployException {
        try {
            HotDeploy hotdeploy = ConfigTool.getRoot().getHotDeploy();
            List<String> watchedResourceList = new ArrayList<String>();
            for (String watchedResource : hotdeploy.getWatchedResource().split(",")) {
                watchedResourceList.add(watchedResource);
            }
            this.hotDeployService.init(hotdeploy.isEnabled(), watchedResourceList);
            this.hotDeployService.start();
            this.hotDeployService.startConfigListener();
        } catch (Exception e) {
            e.printStackTrace();
            this.hotDeployService.stop();
        }
    }

    public void initDeployJMXInterface() throws DeployException {
        try {
            this.deployJMXServer.registerDeployCommand();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }





}
